/* Copyright (C) 2007-2010 STS Soft

   This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
   the Free Software Foundation; version 2 of the License.

   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.

   You should have received a copy of the GNU General Public License
   along with this program; if not, write to the Free Software
   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA */

using System;
using System.Diagnostics;
using System.IO;
using System.Runtime.InteropServices;

namespace STSdb.Files
{
    //TODO: internal class
    //We take the following strategy: to have no problems with the borderline cases and to make no controls, we use large enough types. 
    //If big hdd drives appears we increase the sizes
    [StructLayout(LayoutKind.Sequential)]
#if !WindowsCE && !PocketPC
    [DebuggerDisplay("Position = {Position}, Size = {Size}"), Serializable]
#endif
    public struct Ptr : IEquatable<Ptr>, IComparable<Ptr>
    {
        /// <summary>
        /// number of first byte
        /// </summary>
        public readonly ulong Position;
        /// <summary>
        /// length of the fragment in bytes
        /// </summary>
        public readonly ulong Size;

        public const ulong SIZE = sizeof(ulong) + sizeof(ulong);//(ulong)Marshal.SizeOf(typeof(Ptr2));
        public static readonly Ptr NULL = new Ptr(0, 0);

        public Ptr(ulong position, ulong size)
        {
            Position = position;
            Size = size;
        }

        public override string ToString()
        {
            return string.Format("(Position, Size) = ({0}, {1})", Position, Size);
        }

        public override int GetHashCode()
        {
            return Position.GetHashCode() ^ Size.GetHashCode();
        }

        public override bool Equals(object obj)
        {
            if (obj is Ptr)
                return this.Equals((Ptr)obj);

            return false;
        }

        #region IEquatable<Ptr2> Members

        public bool Equals(Ptr other)
        {
            return Position == other.Position &&
                Size == other.Size;
        }

        #endregion

        #region IComparable<Ptr2> Members

        public int CompareTo(Ptr other)
        {
            return Position.CompareTo(other.Position);
        }

        #endregion

        public static bool operator ==(Ptr ptr1, Ptr ptr2)
        {
            return ptr1.Equals(ptr2);
        }

        public static bool operator !=(Ptr ptr1, Ptr ptr2)
        {
            return !(ptr1 == ptr2);
        }

        /// <summary>
        /// checking whether the pointer is invalid
        /// </summary>
        public bool IsNull
        {
            get { return this.Equals(NULL); }
        }

        /// <summary>
        /// returns index of the block after fragment
        /// </summary>
        public ulong PositionPlusSize
        {
            get { return checked(Position + Size); }
        }

        #region Serialize/Deserialize

        public void Serialize(BinaryWriter writer)
        {
            writer.Write(Position);
            writer.Write(Size);
        }

        public static Ptr Deserialize(BinaryReader reader)
        {
            ulong position = reader.ReadUInt64();
            ulong size = reader.ReadUInt64();

            return new Ptr(position, size);
        }

        public void Serialize(RawFile writer, ulong position)
        {
            writer.Write(position, BitConverter.GetBytes(Position), 0, sizeof(ulong));
            writer.Write(position + sizeof(ulong), BitConverter.GetBytes(Size), 0, sizeof(ulong));
        }

        public static Ptr Deserialize(RawFile reader, ulong position)
        {
            byte[] buffer = new byte[sizeof(ulong) + sizeof(ulong)];
            reader.Read(position, buffer, 0, buffer.Length); 

            return new Ptr(BitConverter.ToUInt64(buffer, 0), BitConverter.ToUInt64(buffer, sizeof(ulong)));
        }

        #endregion

        public bool Contains(ulong position)
        {
            return Position <= position && position < PositionPlusSize;
        }
    }
}
